function reloadTab(tabId) {
  if (typeof tabId == "number") {
    chrome.tabs.get(tabId, function(tab){
      //console.log("Reloading tab:" + tab.id +" for url:" + tab.url);
      chrome.tabs.update(tab.id, {url: tab.url});
    });
  } else throw new Error("reloadTab(tabId): tabId must be a number");
}
function reloadWindow(window) {
  chrome.tabs.getAllInWindow(window.Id, function(tabs) {
    for (var i in tabs) {
      reloadTab(tabs[i]);
    }
  });
}
function reloadAllWindows() {
  chrome.windows.getAll({}, function(windows) {
    for (var i in windows) {
      //console.log("Reloading window:" + window + ".");
      reloadWindow(windows[i]);
    }
  });
}

var jobs = [];
/* Ex:  job object {
          processes:[process], loop:true, delay:5000,
          intervalId:x,comment:"Reload selected tab every 5 sec."
        });
*/
/* Ex:  process object {
          action:"reload", delay:0, tabIds:[selectedTab.id],
          comment:"Reload selected tab"
        }
*/

function allTabIds(operation) {
  /**
  * Usage: allTabIds(function(tabId, process, job, processNum, jobNum){...});
  *
  * Description: Executes the supplied function on every tabId,
  *   for every process, for every job
  */
  allProcesses(function(process, job, processNum, jobNum){
    tabIds = process.tabIds;
    for (i in tabIds) {
      if (tabIds[i]) {
        operation(tabIds[i], process, job, processNum, jobNum);
      }
    }
  });
}
function allProcesses(operation) {
  /**
  * Usage: allProcesses(function(process, job, processNum, jobNum){...});
  *
  * Description: Executes the supplied function on every process,
  *   for every job
  */
  allJobs(function(job, jobNum) {
    processes = job.processes;
    for (i in processes) {
      if (processes[i]) {
        operation(processes[i], job, i, jobNum);
      }
    }
  });
}
function allJobs(operation) {
  /**
  * Usage: allJobs(function(job, jobNum){...});
  *
  * Description: Executes the supplied function for every job
  */
  for (i in jobs) {
    if (jobs[i]) { // Skip stopped/deleted jobs
      operation(jobs[i], i);
    }
  }
}

function isTabInActiveJob(tabId) {
  var active = false;
  allTabIds(function(id) {
    if (id == tabId) active = true;
  });
  return active;
}

function startJob(job) {
  // Fixme: this function is far from complete (only works for reloadTab)
  if (typeof job == "object") {
    jobs[jobs.length] = job;
    //console.log("Job number " + jobs.length + " has been added.");
    jobNum = jobs.length - 1;

    // Fixme: obviously this only works for jobs affecting only one tab
    tabId = job.processes[0].tabIds[0];
    // Fixme: the timer should use a recursive function with setTimeout,
    //  so the delay is guaranteed to be the minimum amount of time between
    //  executions of the process.
    job.intervalId = setInterval('reloadTab(' + tabId + ')', job.delay);
    //console.log("Job number " + (jobNum + 1)  + " has started " +
    //  "on interval " + job.intervalId +
    //  " with a delay of " + job.delay + "ms" + ": " + job.comment
    //);
  } else throw new Error("addJob(job): job must be an object");
}

function stopJob(jobNum /* optional */, tabId /* optional */) {
  /**
  * Usage: stopJob(jobNum); or stopJob(tabId); (one arg. is required)
  *
  * Description: Stops the job "jobs[jobNum]", or any/all jobs that relate
  *   soley to tabId. This will not stop jobs that relate to tabs other than
  *   tabId. Note: Supplying a tabId means that more than one job may be
  *   stopped, since more than one job may run on a particular tab.
  */

  // Actually stops the job when called
  var stop = function(jobNum, job /* optional */) {
    /* if (typeof jobNum != "number") {
        throw new Error("stopJob|stop(jobNum, job): jobNum must be a number");
       } else {} */ // This doesn't work because "for (i in jobs)" means i
       // is a string for some strange reason

    if (typeof job != "object") {job = jobs[jobNum];}
    clearInterval(job.intervalId);
    // Set job object to null
    jobs[jobNum] = null;
    //console.log(
    //  "Job number " + (jobNum + 1) + " has stopped: \"" + job.comment + "\""
    //);
  }

  // Tries to stop job by refering to jobNum, first
  if (typeof jobNum == "number") {
    if (jobNum > jobs.length || jobNum < 0) {
      throw new Error(
        "stopJob(jobNum, undefined): jobNum must be a valid jobs element"
      );
    } else stop(jobNum);
  } else if (typeof tabId == "number") {
    // Since jobNum is undefined, try looking for tabId
    var skipJob = -1;
    allProcesses(function(process, job, processNum, jobNum) {
        // Ignore jobs dealing with more than one tab
        if (skipJob != jobNum) {
          if (process.tabIds.length != 1 || process.tabIds[0] != tabId ) {
            skipJob = jobNum; // Do nothing more with this job
          } else {
            // If this is true, all process for job[i] are known to relate only
            //  to tabId. Therefore, job[i] may be stopped.
            if (p = processes.length) stop(jobNum, job);
          }
        }
    });

  } else {
    throw new Error(
      "stopJob(jobNum, tabId): Either jobNum or tabId must be a number"
    );
  }
  // A little cleanup: let jobs = [] if there are no active jobs
  var activeJobFound;
  allJobs(function() {activeJobFound = true;});
  if (!activeJobFound) jobs = [];
}